package wecom

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"sync"
	"time"

	"monitor.ilin.cc/config"
)

const (
	webhookURLFormat = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=%s"
)

type Robot struct {
	name        string
	webhookURL  string
	secret      string
	retryTimes  int
	client      *http.Client
	rateLimiter *RateLimiter
}

type RateLimiter struct {
	mu       sync.Mutex
	lastSent time.Time
	interval time.Duration
}

func NewRateLimiter(interval time.Duration) *RateLimiter {
	return &RateLimiter{
		interval: interval,
	}
}

func (r *RateLimiter) Wait() {
	r.mu.Lock()
	defer r.mu.Unlock()

	now := time.Now()
	if diff := now.Sub(r.lastSent); diff < r.interval {
		time.Sleep(r.interval - diff)
	}
	r.lastSent = time.Now()
}

// Name 返回机器人名称
func (r *Robot) Name() string {
	return r.name
}

// NewRobot 创建机器人实例
func NewRobot(cfg *config.WeComRobot) *Robot {
	return &Robot{
		name:       cfg.Name,
		webhookURL: fmt.Sprintf(webhookURLFormat, cfg.Key),
		secret:     cfg.Secret,
		retryTimes: cfg.RetryTimes,
		client: &http.Client{
			Timeout: time.Second * 5,
		},
		rateLimiter: NewRateLimiter(time.Second * 2), // 每2秒最多发送一条消息
	}
}

// SendMessage 发送消息
func (r *Robot) SendMessage(msg interface{}) error {
	// 等待限流器
	r.rateLimiter.Wait()

	data, err := json.Marshal(msg)
	if err != nil {
		return fmt.Errorf("marshal message failed: %v", err)
	}

	// 重试机制
	var lastErr error
	for i := 0; i <= r.retryTimes; i++ {
		if err := r.doSend(data); err != nil {
			lastErr = err
			time.Sleep(time.Second * time.Duration(i+1))
			continue
		}
		return nil
	}
	return fmt.Errorf("send message failed after %d retries: %v", r.retryTimes, lastErr)
}

func (r *Robot) doSend(data []byte) error {
	req, err := http.NewRequest(http.MethodPost, r.webhookURL, bytes.NewReader(data))
	if err != nil {
		return err
	}

	req.Header.Set("Content-Type", "application/json")

	resp, err := r.client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	// 检查响应
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("request failed with status: %d", resp.StatusCode)
	}

	var result struct {
		ErrCode int    `json:"errcode"`
		ErrMsg  string `json:"errmsg"`
	}

	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
		return err
	}

	if result.ErrCode != 0 {
		return fmt.Errorf("wecom api error: %d - %s", result.ErrCode, result.ErrMsg)
	}

	return nil
}
